package com.ec50n9.server.common

import org.springframework.beans.factory.config.BeanDefinition
import org.springframework.beans.factory.support.BeanDefinitionBuilder
import org.springframework.beans.factory.support.BeanDefinitionRegistry
import org.springframework.context.ApplicationContext
import org.springframework.context.ApplicationContextAware
import org.springframework.context.ConfigurableApplicationContext
import org.springframework.stereotype.Component


@Component
class SpringUtils : ApplicationContextAware {

    companion object {
        lateinit var applicationContext: ApplicationContext

        /**
         * 通过[name]获取bean
         */
        fun getBean(name: String) = applicationContext.getBean(name)

        /**
         * 通过[clazz]获取bean
         */
        fun <T> getBean(clazz: Class<T>) = applicationContext.getBean(clazz)

        /**
         * 主动向Spring容器中注册bean
         * [name]为BeanName, [clazz]为注册的bean的类型, [args]为构造方法的必要参数, 顺序和类型要求和[clazz]中定义的一致
         */
        fun <T> registerBean(
            name: String, clazz: Class<T>?,
            vararg args: Any?
        ): T {
            val context = applicationContext as ConfigurableApplicationContext
            if (context.containsBean(name)) {
                val bean = context.getBean(name)
                return if (clazz?.let { bean.javaClass.isAssignableFrom(it) } == true) {
                    bean as T
                } else {
                    throw RuntimeException("BeanName 重复 $name")
                }
            }
            val beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz!!)
            for (arg in args) {
                beanDefinitionBuilder.addConstructorArgValue(arg)
            }
            val beanDefinition: BeanDefinition = beanDefinitionBuilder.rawBeanDefinition
            val beanFactory = context.beanFactory as BeanDefinitionRegistry
            beanFactory.registerBeanDefinition(name, beanDefinition)
            return context.getBean(name, clazz)
        }
    }

    override fun setApplicationContext(applicationContext: ApplicationContext) {
        SpringUtils.applicationContext = applicationContext
    }
}